package wxdb.planner;

import java.io.File;
import java.util.*;

import wxdb.ui.Global;
import wxdb.metadata.DbInfo;
import wxdb.metadata.DbInfoMgr;
import wxdb.metadata.IndexInfoMgr;
import wxdb.metadata.MetadataMgr;
import wxdb.metadata.TableMgr;
import wxdb.parse.absyn.*;
import wxdb.query.*;
import wxdb.record.*;
import wxdb.index.btree.*;
import static java.sql.Types.*;

/**
 * The basic planner for SQL update statements.
 * @author sciore
 */
public class BasicUpdatePlanner implements UpdatePlanner{
   
   public int executeDelete(DltExp data) {
	   
	  //select
	   HashSet<RID> resultset = TableMgr.selectTable(data.tableName(), data.pred());
	  //delete
	  BTreeIndex bi;
	  TableInfo ti = TableMgr.getMeta(Global.currentdb, data.tableName());
	  for(RID rid : resultset){
		  RecordPage rp = new RecordPage(rid.getPid(),ti);
		  rp.moveTo(rid.getSlotNum());
		  
		  //update index metadata
		  
    	  Schema sch = ti.getSchema();
    	  Set<String> flds = sch.getFields();
    	  for(String fldName : flds){
	         bi = IndexInfoMgr.getIndexInfo(Global.currentdb,
	 					data.tableName(),fldName);
	         bi.delete(rp.getField(fldName), rid);
	         updateIndexMeta(Global.currentdb, data.tableName(), fldName, bi);
    	  }
    	  
		  rp.delete();
	  }
	  
	  updateTableMeta(Global.currentdb, data.tableName(), ti);
	  
	  return resultset.size();
   }
   
   public int executeModify(UpdtExp data) {
	   
	   //select
	   HashSet<RID> resultset = TableMgr.selectTable(data.tableName(), data.pred());

	  //update
	  BTreeIndex bi;
	  TableInfo ti = TableMgr.getMeta(Global.currentdb, data.tableName());
	  
	  for(RID rid : resultset){
		  ArrayList<Expression> vals = data.newValues();
		  ArrayList<String> targets = data.targetFields();
		  RecordPage rp = new RecordPage(rid.getPid(),ti);
		  rp.moveTo(rid.getSlotNum());
		  
		  boolean continueFlag = false;
		  for (int i=0 ;i<vals.size();i++) {
			 String fldName = targets.get(i);
			 Constant val = vals.get(i).evaluate(null);
	     	 bi = IndexInfoMgr.getIndexInfo(Global.currentdb, 
	 					data.tableName(),fldName);
	     	 if(ti.isKey(fldName)){
	     		 bi.beforeFirst(val);
	     		 if(bi.next()){
		     		 System.out.println("dup key value:"+ val.toString() +", refuse to insert");
		     		 continueFlag = true;
		     		 break;
	     		 }
	     	 }
	      }
		  if(continueFlag)
			  continue;
		  
		  for(int i=0 ;i<vals.size();i++){
			  String fldName = targets.get(i);
			  Constant val = vals.get(i).evaluate(null);    	 
			  //update index metadata
			  bi = IndexInfoMgr.getIndexInfo(Global.currentdb,
	 					data.tableName(),fldName);
			  bi.delete(rp.getField(fldName), rid);
			  
			  rp.setVal(fldName, val);		//update it for RID below     
			  bi.insert(val, rid);
			  updateIndexMeta(Global.currentdb, data.tableName(), fldName, bi);   
		  }
	  }
	  
	  return resultset.size();

   }
   
   public int executeInsert(IstExp data) {
      Plan p = new TablePlan(data.tableName(),null);
      UpdateScan us = (UpdateScan) p.open();
      BTreeIndex bi;
      us.insert();
      Iterator<Constant> iter = data.vals().iterator();
      Iterator<Constant> iter1 = data.vals().iterator();
      TableInfo ti = TableMgr.getMeta(Global.currentdb, data.tableName());
      for (String fldname : data.fields()) {
    	  Constant val = iter1.next();
     	 bi = IndexInfoMgr.getIndexInfo(Global.currentdb, 
 					data.tableName(),fldname);
     	 if(ti.isKey(fldname)){
     		 bi.beforeFirst(val);
     		 if(bi.next()){
	     		 System.out.println("dup key value:"+ val.toString() +", refuse to insert");
	     		 return -1;
     		 }
     	 }
      }
      for (String fldname : data.fields()) {
         Constant val = iter.next();
         us.setVal(fldname, val);
         //update index metadata
         bi = IndexInfoMgr.getIndexInfo(Global.currentdb, 
					data.tableName(),fldname);
         bi.insert(val, ((TableScan)us).getRid());
         updateIndexMeta(Global.currentdb, data.tableName(), fldname, bi);
      }
      //update table metadata
      updateTableMeta(Global.currentdb, data.tableName(), ((TableScan)us).updatedTableInfo());
      return 1;
   }
   
   public int executeCreateTable(CrtExp data) {
	   File f = new File("metadata/" + Global.findDBName(Global.currentdb)+data.tableName()+".ti");
	   if(f.exists()){
		   System.out.println("Table existed");
		   return -1;
	   }
	   Global.mg.addTreeNode(data.tableName());
	   MetadataMgr metaMgr = new MetadataMgr();
	   
	   Schema sch = data.newSchema();
//	   Schema idxSch = new Schema();
	   BTreeIndex idx;
	   
	   //create index metadata
	   for(String fldName : sch.getFields()){
		   Schema idxSch = new Schema();
		   if(sch.getType(fldName) == INTEGER)
			   idxSch.addIntField("dataval", false);
		   else if(sch.getType(fldName) == VARCHAR)
			   idxSch.addStringField("dataval", sch.getLen(fldName), false);
		   idxSch.addIntField("pid", false);
		   idxSch.addIntField("slotnum", false);
		   idx = new BTreeIndex(Global.currentdb, idxSch);
		   updateIndexMeta(Global.currentdb, data.tableName(), fldName, idx);
	   }
	   
	   //create tableinfo metadata
	   TableInfo ti = new TableInfo(Global.currentdb, sch);
	   updateTableMeta(Global.currentdb, data.tableName(), ti);
	   
	   //update dbinfo
	   DbInfo dbinfo = metaMgr.getDbInfo(Global.currentdb);
	   updateDbMeta(Global.currentdb, dbinfo.getTabelNum()+1 ,data.tableName());
	   
	   return 0;
   }
   
   private void updateDbMeta(int dbid, int tableNum, String tblname){
	   DbInfo din = DbInfoMgr.getMeta(dbid);
	   din.setDbid(dbid);
	   din.setTableNum(tableNum);
	   din.addTable(tblname);
	   DbInfoMgr.writeMeta(din);
   }
   
   private void updateTableMeta(int dbid, String tableName, TableInfo ti){
	   TableMgr.writeMeta(dbid, tableName, ti);
   }
   
   private void updateIndexMeta(int dbid, String tableName, String fldName, BTreeIndex index){
	   IndexInfoMgr.writeIndexInfo(dbid, tableName, fldName, index);
   }
   
}
